package cn.abcsys.cloud.devops.web.util;

import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.ConnectException;
import java.net.SocketException;

import org.apache.log4j.Logger;
import org.springframework.util.StringUtils;

import ch.ethz.ssh2.ChannelCondition;
import ch.ethz.ssh2.Connection;
import ch.ethz.ssh2.SCPClient;
import ch.ethz.ssh2.SFTPv3Client;
import ch.ethz.ssh2.Session;
import ch.ethz.ssh2.StreamGobbler;

public class SSH {

	private static final Logger LOGGER = Logger.getLogger(SSH.class);

	private static final int DEFAULT_PORT = 22;

	/**
	 * The host name of the agent, in form of IP address
	 */
	private String hostname;

	/**
	 * The user name of the agent
	 */
	private String username;

	/**
	 * The password of the agent, corresponds to the user-name
	 */
	private String password;

	/**
	 * The port
	 */
	private int port;
	/**
	 * SSH connection between the console and agent
	 */
	private Connection conn;

	private Session session;

	/**
	 * Constructor
	 * 
	 * @author liangzi
	 * @param hostname
	 * @param username
	 * @param password
	 * @version 1.0 2015年8月17日
	 */
	public SSH(String hostname, String username, String password) {
		this(hostname, username, password, DEFAULT_PORT);
	}

	/**
	 * Constructor
	 * 
	 * @author liangzi
	 * @param hostname
	 * @param username
	 * @param password
	 * @param port
	 * @version 1.0 2015年8月17日
	 */
	public SSH(String hostname, String username, String password, int port) {
		this.hostname = hostname;
		this.username = username;
		this.password = password;
		this.port = port;
	}

	/**
	 * @author langzi
	 * @return
	 * @version 1.0 2015年8月17日
	 */
	public boolean connect() throws IOException, SocketException {
		boolean isAuthenticated = false;
		conn = new Connection(hostname, port);
		conn.connect();
		isAuthenticated = conn.authenticateWithPassword(username, password);
		if (isAuthenticated) {
			session = conn.openSession();
		}
		return isAuthenticated;
	}

	/**
	 * @author langzi
	 * @version 1.0 2015年8月17日
	 */
	public void close() {

		if (session != null) {
			session.close();
		}

		if (conn != null) {
			conn.close();
		}
	}

	public void closeSession() {
		if (session != null) {
			session.close();
		}
	}

	public void openSession() throws IOException {
		if (null != conn) {
			session = conn.openSession();
		}
	}

	@Override
	public String toString() {
		return hostname + File.pathSeparator + username + File.pathSeparator + password;
	}

	/********************************************************************************************
	 * 
	 * Other Commands
	 * 
	 ********************************************************************************************/

	/**
	 * 直到指令全部执行完，方能返回结果。 如果执行大文件拷贝指令，则需要等待很久
	 * 
	 * @param commandLine
	 * @return
	 * @throws IOException
	 */
	public boolean execute(String commandLine) throws IOException {
		return execute(commandLine, 0);
	}

	/**
	 * 如果timeout，则返回false
	 * 
	 * 
	 * @param commandLine
	 * @param timeout
	 * @return
	 * @throws Exception
	 */
	public boolean execute(String commandLine, long timeout) throws IOException {
		if (session == null || !StringUtils.hasText(commandLine)) {
			return false;
		}

		session.execCommand(commandLine);
		session.waitForCondition(ChannelCondition.EXIT_STATUS, timeout);
		// 当超时发生时，session.getExitStatus()为null
		return (session.getExitStatus() == null) ? false : ((session.getExitStatus() == 0) ? true : false);
	}

	/**
	 * 直到指令全部执行完，方能返回结果。 如果执行大文件拷贝指令，则需要等待很久
	 * 
	 * @param commandLine
	 * @return
	 * @throws IOException
	 * @throws Exception
	 */
	public String executeWithResult(String commandLine) throws IOException {
		return executeWithResult(commandLine, 0);
	}

	/**
	 * 如果timeout，则返回""
	 * 
	 * @param commandLine
	 * @param timeout
	 * @return
	 * @throws IOException
	 */
	public String executeWithResult(String commandLine, long timeout) throws IOException, ConnectException {
		StringBuilder result = new StringBuilder();
		if (session == null || !StringUtils.hasText(commandLine)) {
			return null;
		}
		session.execCommand(commandLine);
		int condition = session.waitForCondition(ChannelCondition.EXIT_STATUS, timeout);
		if (timeout(condition)) {
			LOGGER.error("Command [" + commandLine + "] is timeout");
		} else {
			InputStream  is = new StreamGobbler(session.getStderr());
			String str="";
			try {
				str = processStream(is,"UTF-8");
			} catch (Exception e) {
				e.printStackTrace();
			}
			result.append(IOUtils.toString(new StreamGobbler(session.getStdout()))).append(IOUtils.toString(is));
			IOUtils.close(is);
			if (str.length() > 0) {
				return str;
			}
		}
		return result.toString();
	}
	
	private String processStream(InputStream in, String charset) throws Exception {  
        byte[] buf = new byte[1024];  
        StringBuilder sb = new StringBuilder();  
        while (in.read(buf) != -1) {  
            sb.append(new String(buf, charset));  
        }  
        return sb.toString();  
    }

	/**
	 * @author langzi
	 * @param condition
	 * @return
	 * @version 1.0 2015年8月17日
	 */
	private boolean timeout(int condition) {
		return ((condition & ChannelCondition.TIMEOUT) == 1) ? true : false;
	}

	/********************************************************************************************
	 * 
	 * Other Commands
	 * 
	 ********************************************************************************************/

	/**
	 * @author langzi
	 * @param localFile
	 * @param remoteDir
	 * @return
	 * @version 1.0 2015年8月17日
	 */
	public boolean scpFile(String localFile, String remoteDir) {
		SCPClient cp = new SCPClient(conn);
		LOGGER.info(cp.getClass());
		try {
			LOGGER.info("localFile:" + localFile);
			LOGGER.info("remoteDir:" + remoteDir);
			cp.put(localFile, remoteDir);
			return true;
		} catch (Exception e) {
			LOGGER.error(e);
			return false;
		}
	}

	/**
	 * @author langzi
	 * @param toDelFileName
	 * @return
	 * @version 1.0 2015年8月17日
	 */
	public boolean rmFile(String toDelFileName) {
		try {
			SFTPv3Client sftpClient = new SFTPv3Client(conn);
			sftpClient.rm(toDelFileName);
			return true;
		} catch (IOException e) {
			LOGGER.error(e);
			return false;
		}
	}

	/**
	 * @author langzi
	 * @param remoteFile
	 * @return
	 * @version 1.0 2015年8月17日
	 */
	public boolean getFile(String remoteFile) {
		SCPClient sc = new SCPClient(conn);
		try {
			sc.get(remoteFile, this.getClass().getResource("/").getPath());
			return true;
		} catch (IOException e) {
			LOGGER.error(e);
			return false;
		}
	}
	/**
	 * @author langzi
	 * @param remoteFile
	 * @return
	 * @version 1.0 2015年8月17日
	 */
	public boolean GetFile(String remoteFile, String localDir) {
		SCPClient sc = new SCPClient(conn);
		try {
			sc.get(remoteFile, localDir);
			return true;
		} catch (IOException e) {
			LOGGER.error(e);
			return false;
		}
	}
	/**
	 * @author yangqinglin
	 * @param remoteFile
	 * @return
	 * @version 1.0 2016年1月15日
	 */
	public boolean fetchFile(String remoteFile, String localDir) {
		SCPClient client = new SCPClient(conn);
		try {
			client.get(remoteFile, localDir);
			return true;
		} catch (IOException e) {
			LOGGER.error(e);
			return false;
		}
	}
	/**
	 * 执行命令标准输出
	 * @param hostname
	 * @param username
	 * @param password
	 * @param command
	 * @return
	 */
	public static Result stdoutConnection(String hostname, String username, String password,String command ){
		String message = "";
		StringBuffer resultData = new StringBuffer();
		boolean success = false;
		try{
			Connection conn = getOpenedConnection(hostname, username, password);
			Session sess = conn.openSession();
			sess.execCommand(command);
			InputStream stdout = new StreamGobbler(sess.getStdout());
			InputStream stderr = new StreamGobbler(sess.getStderr());
			
			BufferedReader stdoutReader = new BufferedReader(new InputStreamReader(stdout));
			BufferedReader stderrReader = new BufferedReader(new InputStreamReader(stderr));
			StringBuffer result = new StringBuffer();
			while (true)
			{
				String line = stdoutReader.readLine();
				if (line == null)
					break;
				System.out.println("out >>> " + line);
				result.append(line+"\n");
			}
			while (true)
			{
				String line = stderrReader.readLine();
				if (line == null)
					break;
				System.out.println("err >>> " + line);
				result.append(line+"\n");
			}
			System.out.println("脚本退出状态 >>> " + sess.getExitStatus());
			message = "脚本退出状态 >>> " + sess.getExitStatus();
			if(sess.getExitStatus().intValue()==0){
				success = true;
			}
			sess.close();
			conn.close();
		}
		catch (IOException e)
		{
			e.printStackTrace(System.err);
		}
		return new Result(success, resultData, message);
	}
	/**
	 * 
	 * @param host
	 * @param username
	 * @param password
	 * @return
	 * @throws IOException
	 */
	private static Connection getOpenedConnection(String host, String username,  
		    String password) throws IOException {  
		if (LOGGER.isInfoEnabled()) {   
			LOGGER.info("connecting to " + host + " with user " + username  
            + " and pwd " + password);  
  
        }
        Connection conn = new Connection(host);  
  
        conn.connect(); // make sure the connection is opened  
  
        boolean isAuthenticated = conn.authenticateWithPassword(username,  
  
        password);  
  
        if (isAuthenticated == false)  
  
            throw new IOException("Authentication failed.");  
  
        return conn;  
  
    }
}
